/**
 *====================================================
 * 文件名称: CachedBeanResults.java
 * 修订记录：
 * No    日期				作者(操作:具体内容)
 * 1.    2015年9月25日			chenxy(创建:创建文件)
 *====================================================
 * 类描述：(说明未实现或其它不应生成javadoc的内容)
 */
package com.yuexunit.finance.common.util;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;

/**
 * @ClassName: CachedBeanResults
 * @Description: TODO(这里用一句话描述这个类的作用)
 * @author chenxy
 * @date 2015年9月25日 下午1:21:36
 */
public class CachedBeanResults {

	static final Map<Class<?>, Object> classCache = new WeakHashMap<Class<?>, Object>();

	static final Set<ClassLoader> acceptedClassLoaders = new HashSet<ClassLoader>();

	private final Map<String, PropertyDescriptor> propertyDescriptorCache = new LinkedHashMap<String, PropertyDescriptor>();

	private static boolean isCacheSafe(Class<?> clazz, ClassLoader classLoader) {
		ClassLoader target = clazz.getClassLoader();
		if (target == null) {
			return true;
		}
		ClassLoader cur = classLoader;
		if (cur == target) {
			return true;
		}
		while (cur != null) {
			cur = cur.getParent();
			if (cur == target) {
				return true;
			}
		}
		return false;
	}

	private static boolean isClassLoaderAccepted(ClassLoader classLoader) {
		ClassLoader[] acceptedLoaderArray;
		synchronized (acceptedClassLoaders) {
			acceptedLoaderArray = acceptedClassLoaders.toArray(new ClassLoader[acceptedClassLoaders.size()]);
		}
		for (ClassLoader acceptedLoader : acceptedLoaderArray) {
			if (isUnderneathClassLoader(classLoader, acceptedLoader)) {
				return true;
			}
		}
		return false;
	}

	private static boolean isUnderneathClassLoader(ClassLoader candidate, ClassLoader parent) {
		if (candidate == parent) {
			return true;
		}
		if (candidate == null) {
			return false;
		}
		ClassLoader classLoaderToCheck = candidate;
		while (classLoaderToCheck != null) {
			classLoaderToCheck = classLoaderToCheck.getParent();
			if (classLoaderToCheck == parent) {
				return true;
			}
		}
		return false;
	}

	private CachedBeanResults(Class<?> beanClass) {
		try {
			BeanInfo beanInfo = Introspector.getBeanInfo(beanClass);
			PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
			for (PropertyDescriptor pd : pds) {
				if (Class.class.equals(beanClass) && ("classLoader".equals(pd.getName()) || "protectionDomain".equals(pd.getName()))) {
					continue;
				}
				this.propertyDescriptorCache.put(pd.getName(), pd);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@SuppressWarnings("unchecked")
	static CachedBeanResults forClass(Class<?> beanClass) throws Exception {
		CachedBeanResults results;
		Object value;
		synchronized (classCache) {
			value = classCache.get(beanClass);
		}
		if (value instanceof Reference) {
			Reference<CachedBeanResults> ref = (Reference<CachedBeanResults>) value;
			results = ref.get();
		} else {
			results = (CachedBeanResults) value;
		}
		if (results == null) {
			if (isCacheSafe(beanClass, CachedBeanResults.class.getClassLoader()) || isClassLoaderAccepted(beanClass.getClassLoader())) {
				results = new CachedBeanResults(beanClass);
				synchronized (classCache) {
					classCache.put(beanClass, results);
				}
			} else {
				results = new CachedBeanResults(beanClass);
				synchronized (classCache) {
					classCache.put(beanClass, new WeakReference<CachedBeanResults>(results));
				}
			}
		}
		return results;
	}

	PropertyDescriptor[] getPropertyDescriptors() {
		return this.propertyDescriptorCache.values().toArray(new PropertyDescriptor[this.propertyDescriptorCache.size()]);
	}

	PropertyDescriptor getPropertyDescriptor(String name) {
		return this.propertyDescriptorCache.get(name);
	}

}
